pmx-canvas 0.1.21 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +135 -0
- package/Readme.md +4 -3
- package/dist/canvas/global.css +23 -40
- package/dist/canvas/index.js +44 -44
- package/dist/types/server/canvas-db.d.ts +33 -0
- package/dist/types/server/canvas-serialization.d.ts +1 -0
- package/dist/types/server/canvas-state.d.ts +18 -14
- package/docs/screenshot.png +0 -0
- package/package.json +2 -2
- package/skills/pmx-canvas/SKILL.md +8 -4
- package/src/cli/agent.ts +25 -1
- package/src/cli/index.ts +3 -1
- package/src/client/canvas/ExpandedNodeOverlay.tsx +25 -15
- package/src/client/nodes/HtmlNode.tsx +1 -1
- package/src/client/theme/global.css +23 -40
- package/src/server/canvas-db.ts +710 -0
- package/src/server/canvas-operations.ts +9 -4
- package/src/server/canvas-schema.ts +4 -4
- package/src/server/canvas-serialization.ts +26 -0
- package/src/server/canvas-state.ts +277 -48
- package/src/server/canvas-validation.ts +6 -0
- package/src/server/html-primitives.ts +14 -4
- package/src/server/server.ts +14 -8
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,139 @@
|
|
|
3
3
|
All notable changes to `pmx-canvas` are documented here. This project follows
|
|
4
4
|
[Semantic Versioning](https://semver.org/).
|
|
5
5
|
|
|
6
|
+
## [0.1.23] - 2026-05-12
|
|
7
|
+
|
|
8
|
+
Persistence overhaul. Canvas state, snapshots, context pins, and the
|
|
9
|
+
large-payload blob store all move from filesystem JSON files into a
|
|
10
|
+
single SQLite database at `.pmx-canvas/canvas.db` (WAL mode). The
|
|
11
|
+
old `state.json`, `snapshots/`, and per-blob files are auto-imported
|
|
12
|
+
on first boot and renamed to `.bak`. Adds dock-aware validation and
|
|
13
|
+
arrange behavior, accepts the documented `?type=` query string on
|
|
14
|
+
node creation, and grows the schema-metadata kebab-case aliases to
|
|
15
|
+
include both singular and plural variants.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **SQLite persistence (`src/server/canvas-db.ts`, 710 lines).**
|
|
20
|
+
Canvas state, snapshots, context pins, and the blob sidecar
|
|
21
|
+
store now live in `.pmx-canvas/canvas.db` (Bun SQLite in WAL
|
|
22
|
+
mode). Shape preserved across the migration: nodes, edges,
|
|
23
|
+
annotations, viewport, context pins, history, snapshots, and
|
|
24
|
+
blob payloads with checksum validation.
|
|
25
|
+
- Override DB path: `PMX_CANVAS_DB_PATH` env var.
|
|
26
|
+
- Backward-compatible legacy path: `PMX_CANVAS_STATE_FILE` (if
|
|
27
|
+
you set a `.db` path there, it's used as the DB; if not, it's
|
|
28
|
+
treated as a legacy JSON path for migration).
|
|
29
|
+
- `stopCanvasServer()` now calls `canvasState.close()` which
|
|
30
|
+
checkpoints WAL data into the DB file — stop the server (or
|
|
31
|
+
flush/close the SDK) before committing `canvas.db`.
|
|
32
|
+
- SQLite WAL/SHM files (`*.db-wal`, `*.db-shm`) are gitignored;
|
|
33
|
+
`canvas.db` itself is git-committable.
|
|
34
|
+
- **Legacy migration on first boot.** Existing
|
|
35
|
+
`.pmx-canvas/state.json`, root `.pmx-canvas.json`,
|
|
36
|
+
`.pmx-canvas/snapshots/`, `.pmx-canvas-snapshots/`, and blob
|
|
37
|
+
files are imported into the SQLite database and renamed to
|
|
38
|
+
`.bak` on first start. The migration is idempotent and only
|
|
39
|
+
runs when the DB is empty.
|
|
40
|
+
- **HTTP node create accepts `?type=` query string.** `POST
|
|
41
|
+
/api/canvas/node?type=html-primitive` with the body's `data`
|
|
42
|
+
fields is accepted as an alternative to passing `type` in the
|
|
43
|
+
body — handy for `curl` and shell-based agents. The body form
|
|
44
|
+
still wins when both are present.
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- **Docked nodes are excluded from layout collision validation.**
|
|
49
|
+
`validateCanvasLayout` no longer flags `dockPosition !== null`
|
|
50
|
+
nodes as overlap or containment violations. Docked HUD-style
|
|
51
|
+
nodes intentionally sit on top of canvas content; the validator
|
|
52
|
+
now models that.
|
|
53
|
+
- **Docked nodes are treated as arrange-locked.** `arrange()`
|
|
54
|
+
now skips translating nodes with `dockPosition !== null` along
|
|
55
|
+
with pinned and explicitly arrange-locked nodes. Dock geometry
|
|
56
|
+
is anchored to the HUD layer, not the world grid.
|
|
57
|
+
- **Schema kebab-case aliases include plural forms.** `--embedded-
|
|
58
|
+
node-ids`, `--embedded-urls`, and `--slide-titles` are now
|
|
59
|
+
documented aliases for the array-shaped HTML sidecar fields,
|
|
60
|
+
alongside the existing singular `--embedded-node-id`,
|
|
61
|
+
`--embedded-url`, and `--slide-title`.
|
|
62
|
+
- **Bun engine bumped to `>=1.3.14`.** `package.json#engines.bun`
|
|
63
|
+
raised from `>=1.3.12` to `>=1.3.14` to pick up the
|
|
64
|
+
`bun:sqlite` improvements the new persistence layer depends on.
|
|
65
|
+
|
|
66
|
+
### Internal
|
|
67
|
+
|
|
68
|
+
- Regression coverage for: `validateCanvasLayout` ignoring docked
|
|
69
|
+
nodes as collision candidates, html primitive node creation
|
|
70
|
+
accepting the documented query-string `?type=` form, and the
|
|
71
|
+
existing canvas-state and operations suites continuing to pass
|
|
72
|
+
against the SQLite-backed persistence (including snapshot
|
|
73
|
+
save/restore, blob round-trip, and undo/redo history).
|
|
74
|
+
|
|
75
|
+
## [0.1.22] - 2026-05-12
|
|
76
|
+
|
|
77
|
+
CLI ergonomics and response-size polish on top of 0.1.21. Adds a
|
|
78
|
+
`pmx-canvas diagram add` alias, declares kebab-case aliases for the
|
|
79
|
+
HTML sidecar fields in the schema, advertises those flags in `node
|
|
80
|
+
add --help --type html`, elides full file bodies from compact node
|
|
81
|
+
responses, validates presentation theme names with a clear error,
|
|
82
|
+
and improves keyboard focus inside present mode.
|
|
83
|
+
|
|
84
|
+
### Added
|
|
85
|
+
|
|
86
|
+
- **`pmx-canvas diagram add` CLI alias.** A thin wrapper that
|
|
87
|
+
delegates to `pmx-canvas external-app add --kind excalidraw` so
|
|
88
|
+
diagram creation has a discoverable top-level command. The `diagram`
|
|
89
|
+
subcommand is now registered in `AGENT_COMMANDS` and surfaces in
|
|
90
|
+
`pmx-canvas --help`. The help text for `diagram add` notes the
|
|
91
|
+
equivalence so agents can switch between the two without guessing.
|
|
92
|
+
- **`node add --help --type html` advertises sidecar flags.** The
|
|
93
|
+
help output now includes a dedicated "HTML sidecar flags" section
|
|
94
|
+
listing `--summary`, `--agent-summary`, `--description`,
|
|
95
|
+
`--presentation true`, `--slide-title`, and `--embedded-node-id`,
|
|
96
|
+
matching the sidecars added in 0.1.21.
|
|
97
|
+
- **Kebab-case aliases for HTML sidecar fields in
|
|
98
|
+
`canvas_describe_schema`.** The schema entries for `agentSummary`,
|
|
99
|
+
`embeddedNodeIds`, `embeddedUrls`, and `slideTitles` now declare
|
|
100
|
+
the corresponding kebab-case flag names so agents reading the
|
|
101
|
+
schema discover the CLI shape without trial and error.
|
|
102
|
+
|
|
103
|
+
### Changed
|
|
104
|
+
|
|
105
|
+
- **Compact node responses elide file content.**
|
|
106
|
+
`serializeCanvasNodeCompact` replaces a file node's full
|
|
107
|
+
`data.fileContent` with `{ omitted: 'file-content', bytes,
|
|
108
|
+
lineCount, sha256 }`. Agents that hit `canvas_get_node`,
|
|
109
|
+
`canvas_get_layout`, or batch-style responses without
|
|
110
|
+
`full: true` no longer re-receive the file body on every read;
|
|
111
|
+
the file `path` is still exposed as `content` so the node remains
|
|
112
|
+
fetchable.
|
|
113
|
+
- **Presentation theme names are validated.** Passing an invalid
|
|
114
|
+
`theme` (or `theme.base`) to a `presentation` primitive now
|
|
115
|
+
fails fast with a clear "use canvas, midnight, paper, aurora, or
|
|
116
|
+
a custom theme object" message instead of silently falling
|
|
117
|
+
through to a default. The HTTP `POST /api/canvas/node?type=html-
|
|
118
|
+
primitive` endpoint wraps `buildHtmlPrimitive` in a try/catch and
|
|
119
|
+
returns a 400 with the message.
|
|
120
|
+
- **Present-mode keyboard focus is tighter.** Tabbing inside the
|
|
121
|
+
presentation overlay now jumps to the Exit button instead of
|
|
122
|
+
escaping to the underlying canvas, Space and Enter on the Exit
|
|
123
|
+
button no longer trigger slide navigation, and the overlay
|
|
124
|
+
re-focuses itself when keyboard focus drifts outside. The deck
|
|
125
|
+
iframe loses its 18px corner radius in present mode for an
|
|
126
|
+
edge-to-edge fullscreen frame.
|
|
127
|
+
|
|
128
|
+
### Internal
|
|
129
|
+
|
|
130
|
+
- Regression coverage for: `node add` forwarding HTML sidecar flags
|
|
131
|
+
through to the underlying html node, `node add --help --type
|
|
132
|
+
html` advertising the new flags, `diagram add` always invoking
|
|
133
|
+
the Excalidraw external app alias, `diagram` routing through the
|
|
134
|
+
agent CLI (not the server-startup path), presentation primitives
|
|
135
|
+
rejecting unknown theme names, and batch `file` node add
|
|
136
|
+
responses returning compact `file-content` metadata instead of
|
|
137
|
+
the full body.
|
|
138
|
+
|
|
6
139
|
## [0.1.21] - 2026-05-09
|
|
7
140
|
|
|
8
141
|
HTML communication maturity pass on top of 0.1.20. Adds a
|
|
@@ -981,6 +1114,8 @@ otherwise have to discover by trial and error.
|
|
|
981
1114
|
- Regression coverage for snapshot flat-`id` aliases on both MCP and
|
|
982
1115
|
HTTP surfaces, plus async / top-level-`await` WebView script bodies.
|
|
983
1116
|
|
|
1117
|
+
[0.1.23]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.23
|
|
1118
|
+
[0.1.22]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.22
|
|
984
1119
|
[0.1.21]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.21
|
|
985
1120
|
[0.1.20]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.20
|
|
986
1121
|
[0.1.19]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.19
|
package/Readme.md
CHANGED
|
@@ -57,11 +57,12 @@ picks up immediately.
|
|
|
57
57
|
|
|
58
58
|
### 05 / Save
|
|
59
59
|
|
|
60
|
-
Spatial state auto-saves to `.pmx-canvas/
|
|
60
|
+
Spatial state auto-saves to `.pmx-canvas/canvas.db` (debounced ~500 ms) —
|
|
61
61
|
git-committable, shareable across machines, and survives both browser
|
|
62
62
|
refresh and server restart. Named [snapshots](docs/mcp.md#tools), full
|
|
63
63
|
undo/redo, and an auto-detected code graph (JS/TS, Python, Go, Rust) make
|
|
64
|
-
the canvas durable rather than throwaway.
|
|
64
|
+
the canvas durable rather than throwaway. Stop the server before committing
|
|
65
|
+
the DB so SQLite WAL data is checkpointed into the file.
|
|
65
66
|
|
|
66
67
|
### 06 / Any agent
|
|
67
68
|
|
|
@@ -168,7 +169,7 @@ the agent can read `canvas://skills` and pull in companion skills
|
|
|
168
169
|
one machine. No built-in multi-user auth or presence — collaboration means
|
|
169
170
|
human ↔ agent on the same machine, plus any other browser tab/agent
|
|
170
171
|
pointed at the same `localhost:4313`. To share across machines, commit
|
|
171
|
-
`.pmx-canvas/
|
|
172
|
+
`.pmx-canvas/canvas.db`.
|
|
172
173
|
- **What leaves your machine.** The core canvas runs entirely on
|
|
173
174
|
`localhost`. Network egress only happens for explicit, opt-in flows:
|
|
174
175
|
`webpage` nodes fetch the URL you give them; `mcp-app` /
|
package/dist/canvas/global.css
CHANGED
|
@@ -2472,75 +2472,58 @@ body,
|
|
|
2472
2472
|
inset: 0;
|
|
2473
2473
|
z-index: 10050;
|
|
2474
2474
|
display: flex;
|
|
2475
|
-
|
|
2476
|
-
gap: 14px;
|
|
2477
|
-
padding: clamp(12px, 2vw, 28px);
|
|
2475
|
+
padding: 0;
|
|
2478
2476
|
background:
|
|
2479
2477
|
radial-gradient(circle at top left, var(--c-accent-25), transparent 36rem),
|
|
2480
2478
|
rgba(3, 7, 18, 0.96);
|
|
2481
2479
|
color: var(--c-text);
|
|
2482
2480
|
}
|
|
2483
2481
|
|
|
2484
|
-
.html-presentation-toolbar {
|
|
2485
|
-
display: flex;
|
|
2486
|
-
align-items: center;
|
|
2487
|
-
justify-content: space-between;
|
|
2488
|
-
gap: 16px;
|
|
2489
|
-
flex-shrink: 0;
|
|
2490
|
-
padding: 10px 12px;
|
|
2491
|
-
border: 1px solid var(--c-line);
|
|
2492
|
-
border-radius: 16px;
|
|
2493
|
-
background: var(--c-panel-glass);
|
|
2494
|
-
box-shadow: 0 18px 50px var(--c-shadow-heavy);
|
|
2495
|
-
}
|
|
2496
|
-
|
|
2497
|
-
.html-presentation-kicker {
|
|
2498
|
-
color: var(--c-accent);
|
|
2499
|
-
font-size: 10px;
|
|
2500
|
-
font-weight: 800;
|
|
2501
|
-
letter-spacing: 0.14em;
|
|
2502
|
-
text-transform: uppercase;
|
|
2503
|
-
}
|
|
2504
|
-
|
|
2505
|
-
.html-presentation-title {
|
|
2506
|
-
max-width: min(72vw, 900px);
|
|
2507
|
-
overflow: hidden;
|
|
2508
|
-
color: var(--c-text);
|
|
2509
|
-
font-size: 14px;
|
|
2510
|
-
font-weight: 700;
|
|
2511
|
-
text-overflow: ellipsis;
|
|
2512
|
-
white-space: nowrap;
|
|
2513
|
-
}
|
|
2514
|
-
|
|
2515
2482
|
.html-presentation-exit {
|
|
2516
|
-
|
|
2517
|
-
|
|
2483
|
+
position: fixed;
|
|
2484
|
+
top: 12px;
|
|
2485
|
+
right: 12px;
|
|
2486
|
+
z-index: 1;
|
|
2487
|
+
padding: 10px 14px;
|
|
2518
2488
|
border: 1px solid var(--c-line);
|
|
2519
2489
|
border-radius: 999px;
|
|
2520
|
-
background: var(--c-panel-
|
|
2490
|
+
background: var(--c-panel-glass);
|
|
2491
|
+
box-shadow: 0 18px 50px var(--c-shadow-heavy);
|
|
2521
2492
|
color: var(--c-text-soft);
|
|
2522
2493
|
cursor: pointer;
|
|
2523
2494
|
font: 600 12px/1 var(--font);
|
|
2495
|
+
opacity: 0;
|
|
2496
|
+
pointer-events: none;
|
|
2497
|
+
transform: translateY(-6px);
|
|
2498
|
+
transition: opacity 0.15s ease, transform 0.15s ease, border-color 0.15s ease, color 0.15s ease;
|
|
2524
2499
|
}
|
|
2525
2500
|
|
|
2526
|
-
.html-presentation-exit:hover
|
|
2501
|
+
.html-presentation-exit:hover,
|
|
2502
|
+
.html-presentation-exit:focus-visible {
|
|
2527
2503
|
border-color: var(--c-accent);
|
|
2528
2504
|
color: var(--c-text);
|
|
2529
2505
|
}
|
|
2530
2506
|
|
|
2507
|
+
.html-presentation-exit:focus,
|
|
2508
|
+
.html-presentation-exit:focus-visible {
|
|
2509
|
+
opacity: 1;
|
|
2510
|
+
pointer-events: auto;
|
|
2511
|
+
transform: translateY(0);
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2531
2514
|
.html-presentation-stage {
|
|
2532
2515
|
flex: 1;
|
|
2533
2516
|
min-height: 0;
|
|
2534
2517
|
display: flex;
|
|
2535
|
-
border-radius:
|
|
2518
|
+
border-radius: 0;
|
|
2536
2519
|
background: var(--c-bg);
|
|
2537
|
-
box-shadow: 0 24px 90px rgba(0, 0, 0, 0.55);
|
|
2538
2520
|
overflow: hidden;
|
|
2539
2521
|
}
|
|
2540
2522
|
|
|
2541
2523
|
.html-node-frame-presentation {
|
|
2542
2524
|
flex: 1;
|
|
2543
2525
|
min-height: 0;
|
|
2526
|
+
border-radius: 0 !important;
|
|
2544
2527
|
}
|
|
2545
2528
|
|
|
2546
2529
|
/* ── Context pin button on node title bar ────────────────────── */
|